//bange_animation.cpp
//Copyright (C) 2010-2011 Oscar (.teri) Triano

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <bange_animation.hpp>

void bange::animation::RegisterVM(lua_State *vm){
	luaL_Reg method[] = {
	{"AddTime", bangeanimation_AddTime},
	{NULL, NULL}};
	luaL_newmetatable(vm, "bange::animation_method");
	luaL_register(vm, NULL, method);
	lua_pop(vm, 1);
}

inline sf::IntRect bange::animation::ananimation::GetSubrect(){
	return this->frames[this->iframe];
}

void bange::animation::BuildFramesForAnAnimation(bange::animation::ananimation &animation, lua_State *vm){
	//Looking inside animation table for "frames"
	lua_getfield(vm, -1, "frames");
	if (!lua_istable(vm, -1)){
		lua_pop(vm, 1);
		return;}
	std::size_t nframes = lua_objlen(vm, -1);
	animation.frames.reserve(nframes);
	sf::IntRect frame;
	lua_pushnil(vm);
	while(lua_next(vm, -2)){
		if (!lua_istable(vm, -1)){
			lua_pop(vm, 1);
			continue;}
		lua_rawgeti(vm, -1, 1);//Left
		frame.Left = lua_tonumber(vm, -1);
		lua_pop(vm, 1);
		lua_rawgeti(vm, -1, 2);//Top
		frame.Top = lua_tonumber(vm, -1);
		lua_pop(vm, 1);
		lua_rawgeti(vm, -1, 3);//Right
		frame.Right = frame.Left+lua_tonumber(vm, -1);
		lua_pop(vm, 1);
		lua_rawgeti(vm, -1, 4);//Bottom
		frame.Bottom = frame.Top+lua_tonumber(vm, -1);
		lua_pop(vm, 1);
		animation.frames.push_back(frame);
		//next
		lua_pop(vm, 1);
	}
	lua_pop(vm, 1);//frames
}

bange::animation::animation(){
	this->refimage = LUA_REFNIL;
}

bange::animation::~animation(){
	cout << "bange::animation destructor for: " << this << endl;
}

bool bange::animation::NewIndex(lua_State *vm){
	//simplesprite, key, data
	char key[64] = {0};
	strncpy(key, lua_tostring(vm, 2), 64);
	if (strcmp(key, "image") == 0 && lua_isuserdata(vm, 3)){
		bange::image *image = dynamic_cast<bange::image *>(bange::base::GetBaseFromProxy(3, vm));
		if (image == NULL){
			std::cout << "bange: simplesprite.image: The userdata isn't a image" << std::endl;
		}
		luaL_unref(vm, LUA_REGISTRYINDEX, this->refimage);
		this->SetImage(*image);
		this->refimage = luaL_ref(vm, LUA_REGISTRYINDEX);
		return true;
	}
	else if (strcmp(key, "animations") == 0 && lua_istable(vm, 3)){
		std::size_t nanimation = lua_objlen(vm, 3);
		if (nanimation == 0){
			return true;}
		animations.clear();
		animations.reserve(nanimation);
		bange::animation::ananimation ananimation;
		lua_pushnil(vm);
		while(lua_next(vm, -2)){
			if (!lua_istable(vm, -1)){
				lua_pop(vm, 1);//value that isn't a table
				continue;}
			lua_getfield(vm, -1, "step");
			ananimation.step = 0.f;
			if (lua_isnumber(vm, -1)){
				ananimation.step = lua_tonumber(vm, -1);}
			lua_pop(vm, 1);
			ananimation.storetime = 0.f;
			lua_getfield(vm, -1, "auto");
			ananimation.automatic = false;
			if (lua_isboolean(vm, -1)){
				ananimation.automatic = lua_toboolean(vm, -1);}
			lua_pop(vm, 1);
			bange::animation::BuildFramesForAnAnimation(ananimation, vm);
			ananimation.iframe = 0;
			animations.push_back(ananimation);
			//next
			lua_pop(vm, 1);//table animation
		}
		this->ianimation = 0;
		this->SetSubrect();
		return true;
	}
	else if (strcmp(key, "ianimation") == 0 && lua_isnumber(vm, 3)){
		int iframe = lua_tonumber(vm, 3)-1;
		if (iframe < 1 || iframe >= animations.size()){
			return true;}
		this->ianimation = ianimation-1;
		if (this->animations[ianimation].frames.size() == 0){
			return true;}
		this->animations[ianimation].iframe = 0;
		this->animations[ianimation].storetime = 0.f;
		return true;
	}
	else if (strcmp(key, "iframe") == 0 && lua_isnumber(vm, 3)){
		int iframe = lua_tonumber(vm, 3)-1;
		if (iframe < 1 || iframe >= this->animations[iframe].frames.size()){
			return true;}
		this->animations[iframe].iframe = iframe;
		return true;
	}
	return false;
}

bool bange::animation::Index(lua_State *vm){
	//simplesprite, key
	char key[64] = {0};
	strncpy(key, lua_tostring(vm, 2), 64);
	if (strcmp(key, "image") == 0){
		lua_rawgeti(vm, LUA_REGISTRYINDEX, this->refimage);
		return true;}
	else if (strcmp(key, "ianimation") == 0){
		lua_pushnumber(vm, this->ianimation+1);
		return true;}
	lua_getfield(vm, LUA_REGISTRYINDEX, "bange::simplesprite_method");
	lua_getfield(vm, -1, key);
	if (lua_isfunction(vm, -1)){
		return true;}
	lua_pop(vm, 1);
	return false;
}

void bange::animation::Process(float time){
	if (animations.size() == 0){
		return;}
	if (animations[ianimation].storetime >= animations[ianimation].step){
		if (animations[ianimation].frames.size() > animations[ianimation].iframe + 1){
			animations[ianimation].storetime = 0.f;
			animations[ianimation].iframe += 1;
		}
		else{
			animations[ianimation].iframe = 0;
			animations[ianimation].storetime = 0.f;
		}
		this->SetSubrect();
	}
	if (animations[ianimation].automatic){
		animations[ianimation].storetime += time;}
}

void bange::animation::AddTime(float time){
	if (this->animations.size() == 0){
		return;}
	this->animations[ianimation].storetime += time;
}

inline void bange::animation::SetSubrect(){
	if (this->animations[this->ianimation].frames.size() == 0){
		return;}
	this->SetSubRect(this->animations[this->ianimation].GetSubrect());
}

void bange::animation::CleanVM(lua_State *vm){
	luaL_unref(vm, LUA_REGISTRYINDEX, this->refimage);
}

static int bangeanimation_AddTime(lua_State *vm){
	//simplesprite, time
	bange::animation *animation = dynamic_cast<bange::animation *>(bange::base::GetBaseFromProxy(1, vm));
	animation->AddTime(lua_tonumber(vm, 2));
	return 0;
}
